defmodule Pango.SwarmUtil do
	@moduledoc """
	进程模型:
	        msg -> Rpc, Rpc 把消息转发给 impl_woker, 由 impl_woker -> apply(m, f, a), 执行完后,根据需要返回执行结果
	"""
	require Logger
	# GlobalName 里面的, swarm服务名 *Rpc启动的时候的注册名称
	@type service :: atom
	@type func :: atom
	@type args :: map | list
	@type reason :: any
	@type reply :: :ok | {:ok, any} | reason
	@type msg :: {module, func, args}

	@doc """
	caller 阻塞, 在impl_woker进程里执行完后, reply caller
	"""
	@spec sync_call(service, msg) :: reply
	def sync_call(name, {mod, func, args} = msg) do
		#		Logger.info(inspect(msg, limit: :infinity))
		args = if is_list(args) do
			args
		else
			[args]
		end
		if is_join_cluster?(name) do
			GenServer.call({:via, :swarm, name}, {:sync, {mod, func, args}})
		else
			:server_starting
		end
	end

	@doc """
	用于比较耗时的操作,如上传文件等
	caller 阻塞,impl_woker spawn 一个进程A执行(impl_worker 不阻塞), A执行完后, reply caller
	"""
	@spec async_call(service, msg) :: reply | false
	def async_call(name, {mod, func, params} = msg) do
		timeout = Util.to_integer(params["timeout"])
		if is_integer(timeout) && timeout >= 5000 && timeout <= 30000 do
			params = MapLib.replace(params, %{"timeout" => timeout})
			Logger.info(inspect(msg, limit: :infinity))
			if is_join_cluster?(name) do
				GenServer.call({:via, :swarm, name}, {:async, {mod, func, [params]}}, timeout)
			else
				:server_starting
			end
		else
			false
		end
	end

	@doc """
	caller 不阻塞, impl_woker spawn 一个进程A执行, A执行完,不会reply
	"""
	@spec cast(service, msg) :: :ok
	def cast(name, {mod, func, args} = msg) do
		Logger.info(inspect(msg, limit: :infinity))
		args = if is_list(args) do
			args
		else
			[args]
		end
		if is_join_cluster?(name) do
			GenServer.cast({:via, :swarm, name}, {mod, func, args})
		else
			:server_starting
		end
	end

	@spec is_join_cluster?(service) :: boolean
	def is_join_cluster?(name) do
		pid = get_worker(name)
		bool = is_pid(pid)
		bool || Logger.error("name: #{inspect(name)} not join cluster")
		bool
	end

	def get_worker(name), do: Swarm.whereis_name(name)

	def get_group_members(group), do: Swarm.members(group)

	def publish(msg, group), do: Swarm.publish(group, msg)

	def call_group(msg, group), do: Swarm.multi_call(group, msg)


end
